//////////////////////////////////
//
// Main application .c
//
//////////////////////////////////

#include "p18f26k22.h"
#include <GenericTypeDefs.h>
#include <stdio.h>

#include "isrs.h"
#include "pwm.h"
#include "uart.h"
#include "midi_codes.h"
#include "ads8344.h"
#include "mainApp.h"

#pragma config FOSC = INTIO67, PLLCFG = ON, PRICLKEN = ON, FCMEN = OFF, IESO = ON
#pragma config WDTEN = OFF, CCP3MX = PORTC6, CCP2MX = PORTC1, PWRTEN = OFF, BOREN = SBORDIS, BORV = 190
#pragma config PBADEN = OFF             //PORTB<5:0> pins are configured as digital I/O on Reset
#pragma config MCLRE = EXTMCLR, HFOFST = ON, T3CMX = PORTC0, STVREN = ON
#pragma config LVP = OFF, XINST = OFF, DEBUG = OFF


#define NUM_CHANS 6
#define OUTPUT_RATE_REDUCTION_FACTOR 12
#define HAND_OUT_DELAY 7
#define OVERSAMPLE_SUMMATION 8
#define MAX_GRADIENT_EDGE 300
#define MAX_NOISE_PEAK_PEAK 60

#define MIN_COUNT_LINEARISE_14BIT 328 //0.1V
#define MAX_RANGE_LINEARISE_14BIT 16383 //5.0V

/*
 ** channel output frequency **
 *
 * 7812 (adc sample rate)
 * 7812 / NUM_CHANS (channel sample rate)
 * 7812 / (NUM_CHANS * OVERSAMPLE_SUMMATION * OUTPUT_RATE_REDUCTION_FACTOR) channel output rate
 *
 * e.g. NUM_CHANS 6
 *      OUTPUT_RATE_REDUCTION_FACTOR 3
 * channel output rate = 54Hz
 */

static UINT8  switch_state[NUM_CHANS] = {0};

static UINT8  channel_number = 0;
static UINT32 channel_val_accum[NUM_CHANS] = {0};           //next adc channel accumulator
                                                        //only 14 bit numbers can be sent so use u16
static UINT16 previous_channel_value[NUM_CHANS] = {0};           //next adc channel accumulator
static UINT16 output_channel_value[NUM_CHANS] = {0};           //next adc channel accumulator
static UINT16 current_channel_value[NUM_CHANS]= {0};    //value that was last sent to MIDI device
static UINT16 base_channel_value[NUM_CHANS]= {3000};       //value that tracks the dc offset

static UINT8  channel_val_count[NUM_CHANS] = {0};

static UINT16 previous_cont_chng_accum = 0;

static UINT8 first_count_hand_out[NUM_CHANS] = {0};

static UINT8 prev_MS7bits[NUM_CHANS] = {0};
static UINT8 prev_LS7bits[NUM_CHANS] = {0};
static UINT16 prev_MLbits[NUM_CHANS] = {0};

static hand_state_t hand_pos[NUM_CHANS] = {HAND_OUT};

void main (void)
{
    //char buffer[20] = {0};
    UINT8 i[NUM_CHANS] = {0};
    UINT8 j = 0;
    INT16 edge_diff = 0;
    INT16 mod_edge_diff = 0;
    //UINT32 counter_main = 0;

    OSCTUNEbits.PLLEN   = 0b1;//change to 0b1 for PLL * 4
    OSCCONbits.IRCF     = 0b111;
    OSCCONbits.SCS      = 0b00; //change to 0x00 for PLL * 4
    OSCCON2bits.MFIOSEL = 0b0;

    //enable interrupts global
    RCONbits.IPEN   = 0b1;
    INTCONbits.GIEH = 0b1;
    INTCONbits.GIEL = 0b0;

    //init peripherals
    /* MkII board */
    M_RESET   = 0b1;      //Set Master Reset High
    /* MkII board */
    pwm_init();
    ads8344_comms_config();
    init_debug_uart();

    //input switches TRIS
    TRISAbits.RA0 = 0b1;
    TRISAbits.RA1 = 0b1;
    TRISAbits.RA2 = 0b1;
    TRISAbits.RA3 = 0b1;
    TRISAbits.RA4 = 0b1;
    TRISBbits.RB5 = 0b1;

    //debug pins fo toggling
    TRISBbits.RB0 = 0b0;
    TRISBbits.RB1 = 0b0;
    //debug pins fo toggling
    
    //main
    while(1)
    {
        tx_midi_command_byte();

        for (j = 0; j < NUM_CHANS; j++) {check_toggle(j);}

        if(read_adc)
        {
            read_adc = FALSE;
            if(channel_number == NUM_CHANS)
            {
                channel_number = 0;
            }
            channel_val_accum[channel_number] += read_ads8344(channel_select(channel_number));

            //hand position detection detect
            //do this with >> 2 - only using 14bit anyway!

            /*oversample*/
            channel_val_count[channel_number]++;
            if (channel_val_count[channel_number] == OVERSAMPLE_SUMMATION)
            {
                channel_val_count[channel_number] = 0;
                /*convert to 14 bit*/
                channel_val_accum[channel_number] = (channel_val_accum[channel_number] >> 5); //..3 from *8 oversampling >> 2 from 16 bit adc
                current_channel_value[channel_number] = (UINT16)(channel_val_accum[channel_number] & 0x00003FFF);
                /*hand in/out processing*/
                edge_diff = (INT16)previous_channel_value[channel_number] - (INT16)current_channel_value[channel_number];
                if(hand_pos[channel_number] == HAND_IN)
                {
                    if(current_channel_value[channel_number] > (MIN_COUNT_LINEARISE_14BIT + MAX_RANGE_LINEARISE_14BIT))
                    {
                        //edge detected
                            hand_pos[channel_number] = HAND_OUT;
                            first_count_hand_out[channel_number] = 1;
                    }else if((current_channel_value[channel_number] - base_channel_value[channel_number]) < (MAX_NOISE_PEAK_PEAK/6))
                    {
                        //edge detected
                            hand_pos[channel_number] = HAND_OUT;
                            first_count_hand_out[channel_number] = 1;
                    }else if ( edge_diff > (((INT16)previous_channel_value[channel_number] - (INT16)base_channel_value[channel_number]) / 16))
                            //&& (edge_diff > (MAX_NOISE_PEAK_PEAK/3)))
                    {
                            hand_pos[channel_number] = HAND_OUT;
                            first_count_hand_out[channel_number] = HAND_OUT_DELAY;
                    }
                }
                else
                { /*hand out*/
                    if (first_count_hand_out[channel_number] == 0)
                    {
                        mod_edge_diff = (edge_diff < 0) ? (edge_diff * -1) : edge_diff;
                        if((mod_edge_diff > (MAX_NOISE_PEAK_PEAK)) && (current_channel_value[channel_number] < (MIN_COUNT_LINEARISE_14BIT + MAX_RANGE_LINEARISE_14BIT)))
                        {
                            hand_pos[channel_number] = HAND_IN;
                        }
                    }
                    else
                    {
                        first_count_hand_out[channel_number]--;
                    }
                }
                /*hand in/out processing end*/

                if((hand_pos[channel_number] == HAND_IN) || (switch_state[channel_number] == 1))
                {
                    //output_channel_value[channel_number] = current_channel_value[channel_number];
                    //i[channel_number]++;
                    //if(i[channel_number] >= OUTPUT_RATE_REDUCTION_FACTOR) //slowed for matlab's sake!!
                    //{
                        //if(channel_number == 0)
                        //{

                        send_control_change_value(linearise_channel_val(current_channel_value[channel_number]), channel_number);
                        //}
                      //  i[channel_number] = 0;
                    //}
                }

                previous_channel_value[channel_number] = current_channel_value[channel_number];
                if(base_channel_value[channel_number] > previous_channel_value[channel_number])
                {
                    base_channel_value[channel_number] = previous_channel_value[channel_number];
                }
                /*reset accumulator*/
                channel_val_accum[channel_number] = 0;
            }
            channel_number++;
        }
    }
}


void send_control_change_value(UINT16 current_val, UINT8 chan_num)
{
    UINT8 MS7bits;
    UINT8 LS7bits;
    UINT16 MLbits;

    MS7bits = ((current_val & 0x3F80) >> 7);
    LS7bits = (current_val & 0x007F);
    MLbits = (current_val & 0x3FE0);  //compare 9 MSbits

    if (prev_MLbits[chan_num] != MLbits)
    {
        //buffer_midi_command_byte(0xB0 | chan_num);    //Control change channel 1
        buffer_midi_command_byte(0xB0);             //control change channel 0
        buffer_midi_command_byte(0x10 + chan_num);    //General Purpose Controller 1 MSB
        buffer_midi_command_byte(MS7bits);    //Data value.
        buffer_midi_command_byte(0xB0);             //control change channel 0
        buffer_midi_command_byte(0x30 + chan_num);    //General Purpose Controller 1 LSB
        buffer_midi_command_byte(LS7bits);    //Data value.
    }
    //if (prev_LS7bits[chan_num] != LS7bits)
    //{
        //buffer_midi_command_byte(0xB0 | chan_num);    //Control change channel 1
//        buffer_midi_command_byte(0xB0);             //control change channel 0
//        buffer_midi_command_byte(0x30 + chan_num);    //General Purpose Controller 1 LSB
//        buffer_midi_command_byte(LS7bits);    //Data value.
    //}
    prev_MLbits[chan_num] = MLbits;
    //prev_LS7bits[chan_num] = LS7bits;
    //previous_cont_chng_accum = current_val;
}

void send_note_on_off(hand_state_t hand_in_out, UINT8 chan_num)
{
    //buffer_midi_command_byte(0xB0 | chan_num);    //Control change channel 1
    if(switch_state[chan_num] == 1)
    {
        if(hand_in_out == HAND_IN)
        {
            buffer_midi_command_byte(0x90);         //NOTE ON
            buffer_midi_command_byte(60 + chan_num); //NOTE number (60 = middle c)
            buffer_midi_command_byte(64);            //Velcoity
        }
        else if (hand_in_out == HAND_OUT)
        {
            buffer_midi_command_byte(0x80); //NOTE OFF
            buffer_midi_command_byte(60 + chan_num);  //NOTE number (60 = middle c)
            buffer_midi_command_byte(64);            //Velcoity
        }
    }
}


void check_toggle(UINT8 chan_num)
{
    UINT8 debounce_tmp = 0;
    switch(chan_num)
    {
        case 0:
            debounce_tmp = SWITCH_1;
            if(switch_state[chan_num] != debounce_tmp)
            {
                switch_state[chan_num] = debounce_tmp;     //not debounced!
            }
            break;
        case 1:
            debounce_tmp = SWITCH_2;
            if(switch_state[chan_num] != debounce_tmp)
            {
                switch_state[chan_num] = debounce_tmp;     //not debounced!
            }
            break;
        case 2:
            debounce_tmp = SWITCH_3;
            if(switch_state[chan_num] != debounce_tmp)
            {
                switch_state[chan_num] = debounce_tmp;     //not debounced!
            }
            break;
        case 3:
            debounce_tmp = SWITCH_4;
            if(switch_state[chan_num] != debounce_tmp)
            {
                switch_state[chan_num] = debounce_tmp;     //not debounced!
            }
            break;
        case 4:
            debounce_tmp = SWITCH_5;
            if(switch_state[chan_num] != debounce_tmp)
            {
                switch_state[chan_num] = debounce_tmp;     //not debounced!
            }
            break;
        case 5:
            debounce_tmp = SWITCH_6;
            if(switch_state[chan_num] != debounce_tmp)
            {
                switch_state[chan_num] = debounce_tmp;     //not debounced!
            }
            break;
        default:
            break;
    }
}

void send_switch_change(UINT8 chan_num, UINT8 switch_pos)
{
        if(switch_pos == 0)
        {
            buffer_midi_command_byte(0x90);         //NOTE ON
            buffer_midi_command_byte(60 + chan_num); //NOTE number (60 = middle c)
            buffer_midi_command_byte(64);            //Velcoity
        }
}

UINT8 channel_select(UINT8 chan_num)
{
    switch(chan_num)
    {
        case 0:
            return 1;
        case 1:
            return 5;
        case 2:
            return 2;
        case 3:
            return 6;
        case 4:
            return 3;
        case 5:
            return 7;
        default:
            return 0;
    }
}

UINT16 linearise_channel_val(UINT16 channel_val)
{
    UINT16 lin_val;

        if (channel_val > MIN_COUNT_LINEARISE_14BIT)
        {
            lin_val = channel_val - MIN_COUNT_LINEARISE_14BIT;
        }
        else
        {
            lin_val = 0;
        }
        if(lin_val > MAX_RANGE_LINEARISE_14BIT)
        {
           return 0x3FFF;
        }
        else
        {
            return lin_val;
        }
}

